package top.z.template.interpreter;

import top.z.template.Context;
import top.z.template.Error;
import top.z.template.Template;
import top.z.template.parse.Ast.Node;
import top.z.template.parse.ast.Break;
import top.z.template.parse.ast.Continue;
import top.z.template.parse.ast.Return;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class AstInterpreter {

    public static Object interpret(Template template, Context context, OutputStream out) {
        try {
            Object result = interpretNodeList(template.getNode(), template, context, out);
            if (result == Return.RETURN_SENTINEL) {
                return ((Return.ReturnValue) result).getValue();
            } else {
                return null;
            }
        } catch (Throwable t) {
            if (t instanceof Error.TemplateException) {
                throw (Error.TemplateException) t;
            } else {
                throw Error.error("Couldn't interpret node list due to I/O error, " + t.getMessage(), template.getNode().get(0).getSpan());
            }
        } finally {
            // clear out RETURN_SENTINEL as it uses a ThreadLocal and would leak memory otherwise
            Return.RETURN_SENTINEL.setValue(null);
        }
    }

    public static Object interpretNodeList(List<Node> list, Template template, Context context, OutputStream out) throws IOException {
        for (Node node : list) {
            Object value = node.evaluate(template, context, out);
            if (value != null) {
                if (value == Break.BREAK_SENTINEL || value == Continue.CONTINUE_SENTINEL || value == Return.RETURN_SENTINEL) {
                    return value;
                } else {
                    out.write(value.toString().getBytes(StandardCharsets.UTF_8));
                }
            }
        }
        return null;
    }
}
